package com.medusa.aps.business.modules.demand.mp.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.medusa.aps.business.common.model.resp.SystemCode;
import com.medusa.aps.business.common.redis.annotation.Redisson;
import com.medusa.aps.business.global.model.exception.GlobalException;
import com.medusa.aps.business.global.model.exception.collector.CurrentErrors;
import com.medusa.aps.business.global.model.exception.collector.DistinctValidator;
import com.medusa.aps.business.global.model.exception.collector.ErrorCollector;
import com.medusa.aps.business.model.constant.RedisConstants;
import com.medusa.aps.business.model.enums.ProductionType;
import com.medusa.aps.business.model.enums.SchedulingStatusEnum;
import com.medusa.aps.business.model.enums.SourceEnum;
import com.medusa.aps.business.model.pagination.Page;
import com.medusa.aps.business.modules.basic.mp.entity.CustomerInformation;
import com.medusa.aps.business.modules.basic.mp.entity.Material;
import com.medusa.aps.business.modules.basic.mp.service.CustomerInformationService;
import com.medusa.aps.business.modules.basic.mp.service.MaterialService;
import com.medusa.aps.business.modules.demand.model.input.*;
import com.medusa.aps.business.modules.demand.model.output.OrderInfoPageListModel;
import com.medusa.aps.business.modules.demand.model.output.SchedulePageListModel;
import com.medusa.aps.business.modules.demand.mp.entity.OrderInformation;
import com.medusa.aps.business.modules.demand.mp.mapper.OrderInformationMapper;
import com.medusa.aps.business.modules.demand.mp.service.OrderInformationService;
import com.medusa.aps.business.modules.plan.mp.entity.ProcessDetails;
import com.medusa.aps.business.modules.plan.mp.entity.PurchasePlan;
import com.medusa.aps.business.modules.plan.mp.entity.SchedulingTaskView;
import com.medusa.aps.business.modules.plan.mp.entity.SuggestedPlan;
import com.medusa.aps.business.modules.plan.mp.service.ProcessDetailsService;
import com.medusa.aps.business.modules.plan.mp.service.PurchasePlanService;
import com.medusa.aps.business.modules.plan.mp.service.SchedulingTaskViewService;
import com.medusa.aps.business.modules.plan.mp.service.SuggestedPlanService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 订单信息服务实现类
 *
 * @author yuxh
 * @since 2023-08-14 17:37:47
 */
@Service("orderInformationService")
public class OrderInformationServiceImpl extends ServiceImpl<OrderInformationMapper, OrderInformation> implements OrderInformationService {
    @Resource
    private MaterialService materialService;
    @Resource
    private SchedulingTaskViewService schedulingTaskViewService;
    @Resource
    private PurchasePlanService purchasePlanService;
    @Resource
    private SuggestedPlanService suggestedPlanService;
    @Resource
    private ProcessDetailsService processDetailsService;
    @Resource
    private CustomerInformationService customerInformationService;

    @Override
    public Page<OrderInfoPageListModel> orderPage(OrderPageQuery query) {
        return baseMapper.orderPage(query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Redisson(value = RedisConstants.ORDER_LOCK_KEY, waitTime = 1)
    @Redisson(value = RedisConstants.MATERIAL_LOCK_KEY, waitTime = 1)
    @Redisson(value = RedisConstants.CUSTOMER_INFORMATION_LOCK_KEY, waitTime = 1)
    public void updateDemandOrders(List<OrderParam> orderParams) {
        updateOrders(orderParams, true);
    }


    /**
     * 编辑排程订单
     *
     * @param orderParams 订单数据
     */
    @Override
    public void updateScheduleOrders(OrderParam orderParams) {
        updateOrders(List.of(orderParams), false);
    }

    /**
     * 批量新增订单
     *
     * @param orderParams 新增订单参数列表
     */
    public void updateOrders(List<OrderParam> orderParams, boolean isDemandOrder) {
        //根据订单号批量查询订单信息、物料信息、客户信息
        OrderParamValidParam validParam = this.paramSelectBatch(orderParams);
        ErrorCollector collector = ErrorCollector.create()
                .keyMapper(orderNo -> "订单号" + orderNo);
        //校验参数
        List<OrderInformation> orders = orderParams.stream()
                .map(orderParam ->
                        collector.current(
                                orderParam.getOrderNumber(),
                                () -> this.orderSaveUpdateDataValid(true, isDemandOrder, SourceEnum.ARTIFICIAL, validParam, orderParam)

                        )
                ).toList();
        if (collector.hasError()) {
            throw new GlobalException(collector.toString());
        }
        this.updateBatchById(orders);
    }

    @Override
    public void updateIncrementDemandOrder(OrderIncrementParam orderIncrement) {
        updateIncrementPublic(orderIncrement, true);
    }

    @Override
    public void updateIncrementScheduleOrder(OrderIncrementParam orderIncrement) {
        updateIncrementPublic(orderIncrement, false);
    }

    /**
     * 编辑订单优先级
     *
     * @param orderIncrement 编辑变化值
     * @param isDemandOrder  是否是需求订单
     */
    private void updateIncrementPublic(OrderIncrementParam orderIncrement, boolean isDemandOrder) {
        //编辑订单时根据状态填充排程备注
        OrderInformation information = lambdaQuery()
                .select(OrderInformation::getId, OrderInformation::getOrderNumber, OrderInformation::getSchedulingStatus, OrderInformation::getOrderPriority)
                .eq(OrderInformation::getId, orderIncrement.getId())
                .oneOpt().orElseThrow(() -> {
                    throw SystemCode.DATA_NOT_EXIST.msgEx("订单数据不存在");
                });
        if (information.getOrderPriority().equals(orderIncrement.getOrderPriority())) {
            return;
        }
        orderIncrement.validEditPriorityOrderStatus(isDemandOrder, information);
        //校验订单状态
        LambdaUpdateChainWrapper<OrderInformation> chainWrapper = lambdaUpdate()
                .set(OrderInformation::getOrderPriority, orderIncrement.getOrderPriority())
                .eq(OrderInformation::getId, orderIncrement.getId());
        if (information.getSchedulingStatus() == SchedulingStatusEnum.ARRANGED) {
            chainWrapper.set(OrderInformation::getIsWaitArrange, Boolean.TRUE);
        }
        boolean update = chainWrapper.update();
        if (!update) {
            throw SystemCode.DATA_UPDATE_FAILED.msgEx("更新订单优先级失败");
        }
    }

    /**
     * 批量新增订单
     *
     * @param orderParams 新增订单参数列表
     */
    @Override
    @Redisson(value = RedisConstants.ORDER_LOCK_KEY, waitTime = 1)
    @Redisson(value = RedisConstants.MATERIAL_LOCK_KEY, waitTime = 1)
    @Redisson(value = RedisConstants.CUSTOMER_INFORMATION_LOCK_KEY, waitTime = 1)
    @Transactional(rollbackFor = Exception.class)
    public void createOrders(List<OrderParam> orderParams) {
        if (CollUtil.isEmpty(orderParams)) {
            return;
        }
        //校验传入的订单好是否重复
        ErrorCollector errors = DistinctValidator.valid(orderParams, OrderParam::getOrderNumber, "订单号");
        if (errors.hasError()) {
            throw new GlobalException(errors.toString());
        }
        //根据订单号批量查询订单信息、物料信息、客户信息
        OrderParamValidParam validParam = this.paramSelectBatch(orderParams);
        ErrorCollector collector = ErrorCollector.create()
                .keyMapper(orderNo -> "订单号" + orderNo);
        //校验参数
        List<OrderInformation> orders = orderParams.stream()
                .map(orderParam -> {
                            return collector.current(
                                    orderParam.getOrderNumber(),
                                    () -> this.orderSaveUpdateDataValid(false, true, SourceEnum.ARTIFICIAL, validParam, orderParam)
                            );
                        }

                ).toList();
        if (collector.hasError()) {
            throw new GlobalException(collector.toString());
        }
        this.saveBatch(orders);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Redisson(value = RedisConstants.ORDER_LOCK_KEY, waitTime = 1)
    public void removeDemandOrders(Set<Long> ids) {
        removeOrders(ids);
    }

    /**
     * 调整排程删除订单
     *
     * @param ids 订单id集合
     */
    @Override
    public void removeScheduleOrders(Set<Long> ids) {
        removeOrders(ids);
    }


    public void removeOrders(Set<Long> ids) {
        ErrorCollector errors = ErrorCollector.create()
                .keyMapper(key -> "订单" + key);
        Set<SchedulingStatusEnum> notSupportDeleteStatus = Set.of(
                SchedulingStatusEnum.ARRANGED, SchedulingStatusEnum.PRODUCING,
                SchedulingStatusEnum.COMPLATED, SchedulingStatusEnum.CLOSE
        );
        for (Long id : ids) {
            //获取订单信息
            OrderInformation order = this.lambdaQuery()
                    .select(OrderInformation::getOrderNumber, OrderInformation::getSchedulingStatus)
                    .eq(OrderInformation::getId, id)
                    .one();
            if (order == null) {
                continue;
            }

            //获取订单号
            String orderNo = order.getOrderNumber();
            Boolean hasError = errors.current(
                    orderNo,
                    () -> {
                        if (notSupportDeleteStatus.contains(order.getSchedulingStatus())) {
                            CurrentErrors.add("状态为已排产、已完工或无需生产不支持删除！");
                            return true;
                        }
                        return false;
                    }
            );
            if (hasError || errors.hasError()) {
                continue;
            }

            //采购计划
            purchasePlanService.lambdaUpdate().eq(PurchasePlan::getOrderNumber, orderNo).remove();
            //建议计划
            suggestedPlanService.lambdaUpdate().eq(SuggestedPlan::getOrderNumber, orderNo).remove();
            //工序计划详情
            processDetailsService.lambdaUpdate().eq(ProcessDetails::getOrderNumber, orderNo).remove();
            //排产任务
            schedulingTaskViewService.lambdaUpdate().eq(SchedulingTaskView::getOrderNumber, orderNo).remove();
        }
        if (errors.hasError()) {
            throw new GlobalException(errors.toString());
        }
        removeBatchByIds(ids);
    }

    /**
     * 订单新增/编辑数据校验
     *
     * @param isUpdate   是否是编辑
     * @param dataSource 数据来源
     * @param validParam 校验参数 订单信息 物料信息 客户信息
     * @param orderParam 订单原始参数
     * @return 订单信息
     */
    private OrderInformation orderSaveUpdateDataValid(boolean isUpdate, boolean isDemandOrder, SourceEnum dataSource, OrderParamValidParam validParam, OrderParam orderParam) {
        //新增 or 编辑
        String orderNo = orderParam.getOrderNumber();
        OrderInformation order = isUpdate ? validParam.getOrderMap().get(orderNo) :
                new OrderInformation().setOrderNumber(orderNo)
                        .setDataSource(dataSource);
        validateData(isUpdate, isDemandOrder, validParam, orderParam);
        if (CurrentErrors.hasError()) {
            return null;
        }
        String itemCode = orderParam.getItemCode();
        Material item = validParam.getItemMap().get(itemCode);
        String customerCode = orderParam.getCustomerCode();
        CustomerInformation customer = StrUtil.isEmpty(customerCode) ? null : validParam.getCustomerMap().get(customerCode);
        order.setMaterialId(item.getId())
                .setItemCode(itemCode)
                .setItemName(item.getMaterialName())
                .setItemStock(item.getActualInventory())
                .setManufacturingStrategy(item.getManufacturingStrategy())
                .setOrderDate(orderParam.getOrderDate())
                .setOrderDeliveryDate(orderParam.getOrderDeliveryDate())
                .setOrderQuantity(new BigDecimal(orderParam.getOrderQuantity()))
                .setPlannedQuantity(new BigDecimal(orderParam.getOrderQuantity()))
                .setSchedulingStatus(isUpdate ? order.getSchedulingStatus() : SchedulingStatusEnum.NOTARRANGED)
                .setOrderPriority(orderParam.getOrderPriority());
        if (isUpdate && !isDemandOrder && order.getSchedulingStatus() == SchedulingStatusEnum.ARRANGED) {
            order.setIsWaitArrange(true);
        }
        //如果是编辑 并且客户编码不等于订单中的客户编码 则更新客户编码和客户名称
        //若客户编码等于订单中的客户编码 客户为空则直接使用订单中的客户名称 否则使用客户信息中的客户名称
        if (isUpdate && StrUtil.equals(customerCode, order.getCustomerCode())) {
            order.setCustomerCompanyName(customer == null ? order.getCustomerCompanyName() : customer.getCustomerCompanyName());
            return order;
        }
        order.setCustomerId(customer == null ? null : customer.getId())
                .setCustomerCode(customer == null ? null : customerCode)
                .setCustomerCompanyName(customer == null ? null : customer.getCustomerCompanyName());
        return order;
    }

    /**
     * 批量查询订单、物料与客户信息
     *
     * @param orderParams orderParams 订单参数
     * @return 查询结果
     */
    private OrderParamValidParam paramSelectBatch(Iterable<OrderParam> orderParams) {
        Set<String> orderNos = new HashSet<>();
        Set<String> itemCodes = new HashSet<>();
        Set<String> customerCodes = new HashSet<>();
        orderParams.forEach(
                orderParam -> {
                    orderNos.add(orderParam.getOrderNumber());
                    itemCodes.add(orderParam.getItemCode());
                    customerCodes.add(orderParam.getCustomerCode());
                }
        );
        //订单新增时需要校验是否已存在 编辑时需要全部信息
        List<OrderInformation> orders = this.lambdaQuery().in(OrderInformation::getOrderNumber, orderNos).list();
        List<Material> items = materialService.lambdaQuery()
                .select(Material::getId, Material::getMaterialCode, Material::getMaterialName, Material::getActualInventory, Material::getProductionType, Material::getManufacturingStrategy)
                .in(Material::getMaterialCode, itemCodes)
                .list();
        List<CustomerInformation> customers = CollUtil.isEmpty(customerCodes) ? List.of() : customerInformationService.lambdaQuery()
                .select(CustomerInformation::getId, CustomerInformation::getCustomerCode, CustomerInformation::getCustomerCompanyName)
                .in(CustomerInformation::getCustomerCode, customerCodes)
                .list();
        return new OrderParamValidParam()
                .setOrderMap(orders.stream().collect(Collectors.toMap(OrderInformation::getOrderNumber, Function.identity())))
                .setItemMap(items.stream().collect(Collectors.toMap(Material::getMaterialCode, Function.identity())))
                .setCustomerMap(customers.stream().collect(Collectors.toMap(CustomerInformation::getCustomerCode, Function.identity())));

    }

    /**
     * 检查数据 并且渲染数据
     * 1. 检查订单是否已存在
     * 2.检查物料是否存在 存在检查是否是自制物料
     * 3.检查客户是否存在
     *
     * @param orderParam 导入数据
     */
    private void validateData(boolean isUpdate, boolean isDemandOrder, OrderParamValidParam param, OrderParam orderParam) {
        //订单交期是否大于下单日期
        if (orderParam.getOrderDeliveryDate().isBefore(orderParam.getOrderDate())) {
            CurrentErrors.add("订单交期", "不能小于下单日期！");
        }
        //检查订单是否已存在
        String orderNo = orderParam.getOrderNumber();
        OrderInformation order = param.getOrderMap().get(orderNo);
        if (!isUpdate && order != null) {
            CurrentErrors.add("订单编号", "已存在！");
        }
        if (isUpdate) {
            if (order == null) {
                CurrentErrors.add("需求订单", "不存在！");
            } else if (!order.getSchedulingStatus().isEditable() && isDemandOrder) {
                CurrentErrors.add("需求订单", "不是未排产状态！");
            } else if (!List.of(SchedulingStatusEnum.NOTARRANGED, SchedulingStatusEnum.ARRANGED).contains(order.getSchedulingStatus())
                    && !isDemandOrder) {
                CurrentErrors.add("调整排产订单", "不是未排产、已排产状态");
            }
        }
        //检查物料信息
        String itemCode = orderParam.getItemCode();
        Material item = param.getItemMap().get(itemCode);
        if (item == null) {
            CurrentErrors.add("物料编码", "对应物料不存在！");
        } else if (ProductionType.SELF_MADE != item.getProductionType()) {
            CurrentErrors.add("物料编码", "对应的物料的生产类型需为“自制”，请修改后重新提交！");
        }
        //如果是不是编辑 或 订单为空 或客户编码 不等于 订单中的客户编码
        boolean continueValid = !isUpdate || order == null;
        String customerCode = orderParam.getCustomerCode();
        if (StrUtil.isEmpty(customerCode)) {
            return;
        }
        if (continueValid || !customerCode.equals(order.getCustomerCode())) {
            //检查客户信息
            if (StrUtil.isNotEmpty(customerCode) && param.getCustomerMap().get(customerCode) == null) {
                CurrentErrors.add("客户编码", "对应客户不存在！");
            }
        }


    }

    @Override
    public List<OrderInformation> selectByScheduled(List<Integer> scheduledStatus, Set<String> orderNumber) {
        return baseMapper.selectByScheduled(scheduledStatus, orderNumber);
    }

    /**
     * 查询调整排程分页列表
     *
     * @param schedulePageQuery 分页参数
     * @return 调整排程分页数据
     */
    @Override
    public Page<SchedulePageListModel> schedulePage(SchedulePageQuery schedulePageQuery) {
        return baseMapper.schedulePage(schedulePageQuery);
    }
}

